<template>
  <div>
    <!-- 面包屑导航区域 -->
    <el-breadcrumb separator-class="el-icon-arrow-right">
      <!-- 点击 首页 ，跳转到/home，但是home设置了路由跳转，就直接到Welcome页面了 -->
      <el-breadcrumb-item :to="{ path: '/home' }">首页</el-breadcrumb-item>
      <el-breadcrumb-item>商品管理</el-breadcrumb-item>
      <el-breadcrumb-item>参数列表</el-breadcrumb-item>
    </el-breadcrumb>
    <!-- 卡片视图区域 -->
    <el-card shadow="hover">
      <!-- Alert警告消息  -->
      <!-- :closable="false" 不允许将警告alert关闭（隐藏后面的叉叉） -->
      <!-- show-icon 展示出警告的默认图标 -->
      <el-alert
        title="请注意：只允许为第三级分类设置相关参数！"
        type="warning"
        :closable="false"
        show-icon
      >
      </el-alert>
      <!-- 选择商品分类区域的级联选择框区域 -->
      <el-row class="cat_opt">
        <el-col>
          <span>选择商品分类：</span>
          <!-- 级联下拉框 -->
          <!-- 需要注意的是：级联选择器能选择的只有没有children那一项，还有children的是不能点击选择的，只能进行展示 -->
          <!-- v-model="selectedCateKeys" 把级联选择框的选择数据的value值双向绑定到本地data中 -->
          <!--  :options 绑定数据源 -->
          <!-- expand-trigger="hover" 设置为鼠标移入时触发级联展示 -->
          <!-- :props="cateProps" 级联的样式绑定,用于设置下拉框的内容,value等 -->
          <!--  @change 只要级联选择器发生了变化,立即触发 -->
          <!-- clearable 设置为可清空选择 -->
          <el-cascader
            v-model="selectedCateKeys"
            :options="cateList"
            expand-trigger="hover"
            :props="cateProps"
            clearable
            @change="parentCateChanged"
          ></el-cascader>
        </el-col>
      </el-row>

      <!-- teb页签区域 -->
      <!-- v-model="activeName" 将页签选中状态的对应页签的name属性保存到本地的activeName中 -->
      <!-- @tab-click 当点击页面时触发的函数 -->
      <el-tabs
        v-model="activeName"
        @tab-click="handleTabClick"
      >
        <!-- 添加动态参数的面板 -->
        <el-tab-pane
          label="动态属性"
          name="many"
        >
          <!-- 添加参数的按钮 -->
          <!-- :disabled="isBtnDiabled" disabled属性根据布尔值来设置为是否禁用，这个属性同样可以用于tag页签中 -->
          <!-- 这里是通过isBtnDiabled这个计算函数进行判断，然后传回一个布尔值进行判断 -->
          <el-button
            :disabled="isBtnDiabled"
            type="primary"
            size="mini"
            @click="addDialogVisible=true"
          >添加参数</el-button>

          <!-- 动态参数的数据表格 -->
          <el-table
            :data="manyTableData"
            border
            stripe
          >
            <!-- 展开栏 -->
            <el-table-column type="expand">
              <!-- 作用域插槽 -->
              <template slot-scope="scope">
                <!-- 循环tag标签，将每一行对应的attr_vals数组循环写入展示 -->
                <!-- closable 这样tag标签就会有个叉叉 -->
                <el-tag
                  class="el-tag-myExpandClass"
                  v-for="(item,i) in scope.row.attr_vals"
                  :key="i"
                  closable
                  @close="handleClosed(scope.row,i)"
                >{{item}}</el-tag>
                <!-- 文本框-添加按钮通过if else进行切换展示 -->
                <!-- v-if="inputVisible" 通过这个if else 根据inputVisible的布尔值来决定展示input还是button -->
                <!--
                  @keyup.enter.native="handleInputConfirm"
                  @blur="handleInputConfirm"
                  当按下回车键或者输入框失去焦点，都会触发handleInputConfirm函数
                -->
                <el-input
                  class="input-new-tag"
                  v-if="scope.row.inputVisible"
                  v-model="scope.row.inputValue"
                  ref="saveTagInput"
                  size="small"
                  @keyup.enter.native="handleInputConfirm(scope.row)"
                  @blur="handleInputConfirm(scope.row)"
                >
                </el-input>
                <!-- @click="showInput(scope.row)" 当点击按钮时，通过showInput函数来改变inputVisible的布尔值，达到切换显示输入框的效果
                因为我们这里的inputVisible是对每行添加的，所以要传给函数来进行设置为true
                -->
                <el-button
                  v-else
                  size="small"
                  @click="showInput(scope.row)"
                >+ New Tag</el-button>
              </template>
            </el-table-column>
            <!-- 索引列 -->
            <el-table-column
              type="index"
              label="序号"
              width="60"
            ></el-table-column>

            <el-table-column
              label="参数名"
              prop="attr_name"
            ></el-table-column>

            <el-table-column label="操作">
              <!-- 作用域插槽 -->
              <template slot-scope="scope">
                <el-button
                  type="primary"
                  icon="el-icon-edit"
                  @click="showEditDialog(scope.row.attr_id)"
                >编辑</el-button>

                <el-button
                  type="danger"
                  icon="el-icon-delete"
                  @click="removeParams(scope.row.attr_id)"
                >删除</el-button>
              </template>
            </el-table-column>

          </el-table>

        </el-tab-pane>

        <!-- 添加静态属性的面板 -->
        <el-tab-pane
          label="静态属性"
          name="only"
        >
          <!-- 添加属性的按钮 -->
          <el-button
            :disabled="isBtnDiabled"
            type="primary"
            size="mini"
            @click="addDialogVisible=true"
          >添加属性</el-button>

          <!-- 静态参数的数据表格 -->
          <el-table
            :data="onlyTableData"
            border
            stripe
          >
            <!-- 展开栏 -->
            <el-table-column type="expand">
              <!-- 作用域插槽 -->
              <template slot-scope="scope">
                <!-- 循环tag标签，将每一行对应的attr_vals数组循环写入展示 -->
                <!-- closable 这样tag标签就会有个叉叉 -->
                <el-tag
                  class="el-tag-myExpandClass"
                  v-for="(item,i) in scope.row.attr_vals"
                  :key="i"
                  closable
                  @close="handleClosed(scope.row,i)"
                >{{item}}</el-tag>
                <!-- 文本框-添加按钮通过if else进行切换展示 -->
                <!-- v-if="inputVisible" 通过这个if else 根据inputVisible的布尔值来决定展示input还是button -->
                <!--
                  @keyup.enter.native="handleInputConfirm"
                  @blur="handleInputConfirm"
                  当按下回车键或者输入框失去焦点，都会触发handleInputConfirm函数
                -->
                <el-input
                  class="input-new-tag"
                  v-if="scope.row.inputVisible"
                  v-model="scope.row.inputValue"
                  ref="saveTagInput"
                  size="small"
                  @keyup.enter.native="handleInputConfirm(scope.row)"
                  @blur="handleInputConfirm(scope.row)"
                >
                </el-input>
                <!-- @click="showInput(scope.row)" 当点击按钮时，通过showInput函数来改变inputVisible的布尔值，达到切换显示输入框的效果
                因为我们这里的inputVisible是对每行添加的，所以要传给函数来进行设置为true
                -->
                <el-button
                  v-else
                  size="small"
                  @click="showInput(scope.row)"
                >+ New Tag</el-button>
              </template>
            </el-table-column>
            <!-- 索引列 -->
            <el-table-column type="index"></el-table-column>

            <el-table-column
              label="属性名称"
              prop="attr_name"
            ></el-table-column>

            <el-table-column label="操作">
              <!-- 作用域插槽 -->
              <template slot-scope="scope">
                <el-button
                  type="primary"
                  icon="el-icon-edit"
                  @click="showEditDialog(scope.row.attr_id)"
                >编辑</el-button>

                <el-button
                  type="danger"
                  icon="el-icon-delete"
                  @click="removeParams(scope.row.attr_id)"
                >删除</el-button>
              </template>
            </el-table-column>

          </el-table>

        </el-tab-pane>
      </el-tabs>
    </el-card>

    <!-- 添加参数的对话框  这里我们想要让动态和静态的添加按纽都用这个对话框-->
    <!-- :title="'添加'+titleText" 因为titleText是个动态计算的数据，所以title要加上 : 动态绑定 -->
    <el-dialog
      :title="'添加'+titleText"
      :visible.sync="addDialogVisible"
      width="50%"
      @close="addDialogClosed"
    >
      <el-form
        :model="addForm"
        :rules="addFormRules"
        ref="addFormRef"
        label-width="100px"
      >
        <el-form-item
          :label="titleText"
          prop="attr_name"
        >
          <el-input v-model="addForm.attr_name"></el-input>
        </el-form-item>
      </el-form>
      <!-- 尾部确认 取消按钮 -->
      <span
        slot="footer"
        class="dialog-footer"
      >
        <el-button @click="addDialogVisible = false">取 消</el-button>
        <el-button
          type="primary"
          @click="addParams"
        >确 定</el-button>
      </span>
    </el-dialog>

    <!-- 修改参数的对话框  这里我们想要让动态和静态的添加按纽都用这个对话框-->
    <!-- :title="'添加'+titleText" 因为titleText是个动态计算的数据，所以title要加上 : 动态绑定 -->
    <el-dialog
      :title="'修改'+titleText"
      :visible.sync="editDialogVisible"
      width="50%"
      @close="editDialogClosed"
    >
      <el-form
        :model="editForm"
        :rules="editFormRules"
        ref="editFormRef"
        label-width="100px"
      >
        <el-form-item
          :label="titleText"
          prop="attr_name"
        >
          <el-input v-model="editForm.attr_name"></el-input>
        </el-form-item>
      </el-form>
      <!-- 尾部确认 取消按钮 -->
      <span
        slot="footer"
        class="dialog-footer"
      >
        <el-button @click="editDialogVisible = false">取 消</el-button>
        <el-button
          type="primary"
          @click="editParams"
        >确 定</el-button>
      </span>
    </el-dialog>

  </div>
</template>

<script>
export default {
  created () {
    this.getCateList()
  },
  data () {
    return {
      // 本地存储获取到的商品分类列表
      cateList: [],
      // 级联选择框的配置对象
      cateProps: {
        value: 'cat_id',
        label: 'cat_name',
        children: 'children'
      },
      // 级联选择器选择的value值保存
      selectedCateKeys: [],
      // 被激活的tag页签名称,默认设置为first这个name
      activeName: 'many',
      // 动态参数的数据
      manyTableData: [],
      // 静态参数的数据
      onlyTableData: [],
      // 默认隐藏添加参数对话框
      addDialogVisible: false,
      // 默认隐藏修改参数对话框
      editDialogVisible: false,
      // 添加表单的数据存储对象，默认attr_name为''
      addForm: {
        attr_name: ''
      },
      // 修改表单的数据存储对象
      editForm: {},
      // 添加表单的验证规则
      addFormRules: {
        attr_name: [{
          required: true, message: '请输入参数名称', trigger: 'blur'
        }]
      },
      // 修改表单的验证规则
      editFormRules: {
        attr_name: [{
          required: true, message: '请输入参数名称', trigger: 'blur'
        }]
      }
    }
  },
  methods: {
    // 获取所有的商品分类，用于给级联下拉框做数据源
    async getCateList () {
      const { data: res } = await this.$axios.get('categories')
      if (res.meta.status !== 200) {
        return this.$message.error('获取商品分类失败')
      }
      this.cateList = res.data
    },
    // 级联选择项发生变化触发这个函数
    parentCateChanged () {
      console.log(this.selectedCateKeys)
      // 调用getParamsData函数获取动态参数/静态参数的数据
      this.getParamsData()
    },
    // tag页签点击事件的处理函数
    handleTabClick () {
      // 打印当前选择的tag页签名称
      console.log(this.activeName)
      // 调用getParamsData函数获取动态参数/静态参数的数据
      this.getParamsData()
    },
    // 获取动态参数/静态参数的数据函数
    async getParamsData () {
      // 我们想要用户只能选择第三级分类，选择前两级分类不会被选中
      // 所以通过this.selectedCateKeys这个的长度来判断是不是选择了第三级分类，因为这个数组的长度和级别正好对应，长度为3则为第三级
      if (this.selectedCateKeys.length !== 3) {
        // !如果不是选择的第三级，立马重置级联选择器获取到的value值数组，这样下拉框就又变成了空的，相当于没选
        this.selectedCateKeys = []
        // 因为没有选择三级分类的时候，不显示这些表格，所以清空其数据源
        // 清空动态参数的表格数据源
        this.manyTableData = []
        // 清空静态态参数的表格数据源
        this.onlyTableData = []
        return this.$message.warning('只允许为第三级分类设置相关参数')
      }
      // 选中的是第三级分类,那就进行端口的数据获取，根据级联下拉框所选分类的id
      // !${this.cateId} 在模板字符串中，不仅能拼接数据，
      // !还能拼接计算函数computed（注意：不能调用methods中的函数），获取这个函数return的数据当作参数
      const { data: res } = await this.$axios.get(`categories/${this.cateId}/attributes`, { params: { sel: this.activeName } })
      if (res.meta.status !== 200) {
        return this.$message.error('获取动态/静态参数列表失败')
      }

      // !将获取到的数据中的attr_vals字符串以空格分割成数组保存，用于在展开栏中展示
      res.data.forEach((item) => {
        // ? 让字符串遇到空格就分割保存为数组，本身是字符串，通过这个split之后就被分割为了字符串数组
        // !需要通过三元表达式进行判断一下，如果本身就没有attr_vals数据，
        // !也就是attr_vals字符串本身为''空，那通过.split(' ')分割就会直接分割成一个包含空格的数组，导致会渲染成一个空的tag标签
        // !所以当attr_vals字符串没数据时就直接赋值为空数组
        item.attr_vals = item.attr_vals ? item.attr_vals.split(' ') : []

        //! 因为在展开栏中要有个添加新tag的按钮和输入框，如果只绑定一个本地数据，会导致点击其中一个按钮，所有按钮都变成输入框，且输入框中的数据都一样
        // 所以要在获取到数据的时候，循环遍历每个参数的时候，对每个参数(每一行的展开栏)设置一个用于切换显示按钮和输入框的布尔值、用于存储输入框数据的本地data
        // 控制文本框和按钮的切换显示
        item.inputVisible = false
        // 文本框输入的数据存储
        item.inputValue = ''

        // 上面的三元表达式等价于下面的if-else判断
        //! attr_vals本身是字符串，所以如果为空就会返回false
        // if (!item.attr_vals) {
        //   item.attr_vals = []
        // } else {
        //   item.attr_vals = item.attr_vals.split(' ')
        // }
      })
      // console.log(res.data)

      // 根据所选tag页签的名字，将many（动态数据）、only（静态数据）分别保存给不同的本地数据存储
      if (this.activeName === 'many') {
        this.manyTableData = res.data
      } else {
        this.onlyTableData = res.data
      }
    },
    // 当关闭添加对话框时清空对话框数据
    addDialogClosed () {
      this.$refs.addFormRef.resetFields()
    },
    // 当关闭编辑对话框时清空对话框数据
    editDialogClosed () {
      this.$refs.editFormRef.resetFields()
    },
    // 添加对话框的确认按钮函数，进行后端传输
    addParams () {
      // 表单预验证
      this.$refs.addFormRef.validate(async (valid) => {
        if (!valid) return false
        const { data: res } = await this.$axios.post(`categories/${this.cateId}/attributes`, { attr_name: this.addForm.attr_name, attr_sel: this.activeName })
        // console.log(res)
        if (res.meta.status !== 201) {
          return this.$message.error('添加参数失败')
        }
        this.$message.success('添加参数成功')
        // 关闭对话框
        this.addDialogVisible = false
        // 重新获取表单数据，重新加载
        this.getParamsData()
      })
    },
    // 编辑的对话框函数
    async showEditDialog (id) {
      // 根据对应行传入的id在后端查找当前行的数据
      const { data: res } = await this.$axios.get(`categories/${this.cateId}/attributes/${id}`, { params: { attr_sel: this.activeName } })
      if (res.meta.status !== 200) {
        return this.$message.error('获取当前行参数数据失败')
      }
      // 通过传入对应行的id在后端查到的数据保存到本地的editForm中保存
      this.editForm = res.data
      console.log(this.editForm)
      this.editDialogVisible = true
    },
    // 修改对话框的确认按钮函数，进行后端传输
    editParams () {
      // 表单预验证
      this.$refs.editFormRef.validate(async (valid) => {
        if (!valid) return false
        // 预验证通过，则向后端发起请求
        const { data: res } = await this.$axios.put(`categories/${this.cateId}/attributes/${this.editForm.attr_id}`, { attr_name: this.editForm.attr_name, attr_sel: this.activeName })
        if (res.meta.status !== 200) {
          return this.$message.error('修改失败')
        }
        this.$message.success('修改成功')
        // 重新加载表格，相当于刷新
        this.getParamsData()
        // 关闭编辑对话框
        this.editDialogVisible = false
      })
    },
    // 删除按钮的函数，用于删除参数
    async removeParams (removeId) {
      // 通过confirm弹框询问确认是否删除
      // !.$confirm的返回值是primise类型，所以可以用async await进行简化
      const confirmResult = await this.$confirm('此操作将永久删除该参数, 是否继续?', '删除参数确认', {
        confirmButtonText: '确定删除',
        cancelButtonText: '取消',
        type: 'warning'
      }).catch((err) => {
        return err
      })
      // !根据confirm的返回值判断是否点击确认
      if (confirmResult !== 'confirm') {
        return this.$message.info('已取消删除')
      } else {
        const { data: res } = await this.$axios.delete(`categories/${this.cateId}/attributes/${removeId}`)
        if (res.meta.status !== 200) {
          return this.$message.error('删除该参数失败')
        } else {
          // 刷新表格要放在return之前，不然会报错
          // 因为删除了一条数据，所以重新获取一下列表
          this.getParamsData()
          return this.$message.success('已成功删除该参数')
        }
      }
    },
    // 添加新tag的文本框失去焦点或者按下enter键时触发函数
    handleInputConfirm (row) {
      // 在输入文本后，移出焦点或者按下enter键，先判断下输入是否合法
      // .trim() 去除空格
      // 当输入的内容为空时，重置输入内容为''空，同时切换为按钮
      if (row.inputValue.trim().length === 0) {
        row.inputValue = ''
        // 隐藏文本框，切换显示添加按钮
        row.inputVisible = false
        return
      }
      // 如果没有return，代表输入没有问题，合法，那就进行后续处理
      // 向当前行的tag数据源中push添加文本输入框中输入的数据（这个数据经过.trim()去除了空格）
      row.attr_vals.push(row.inputValue.trim())
      // 清空文本输入框的内容
      row.inputValue = ''
      // 隐藏文本框，切换显示添加按钮
      row.inputVisible = false

      // 调用接口，保存当前行添加的tag数据
      this.saveAttrVals(row)
      // 因为在前几行通过push向tag的数据源里添加了文本框输入的东西，所以不用刷新表格也能看见页面上更新了哪些东西
    },
    // 将对attr_vals（tag的数据源）的操作传送给后端保存
    async saveAttrVals (row) {
      // ! row.attr_vals.join(' ') 因为我们传入的attr_vals是数组，所以通过jion(' ')将数组以空格拼接为一个字符串
      const { data: res } = await this.$axios.put(`categories/${this.cateId}/attributes/${row.attr_id}`, { attr_name: row.attr_name, attr_sel: row.attr_sel, attr_vals: row.attr_vals.join(' ') })
      if (res.meta.status !== 200) {
        return this.$message.error('修改参数失败')
      }
      this.$message.success('修改参数成功')
    },
    // 删除tag标签
    handleClosed (row, i) {
      // 在当前行的展开栏中的tag标签中，根据传过来的i，找到要删的那一项，通过splice删除数组中的指定项
      // 这里是让前端页面中的指定tag消失，没有保存到后端
      row.attr_vals.splice(i, 1)
      // 调用接口，保存当前行删除的tag数据
      this.saveAttrVals(row)
    },
    // 点击添加新tag按钮时触发函数，用于设置inputVisable为true，展示输入框
    showInput (row) {
      // 切换显示文本框
      row.inputVisible = true
      // 切换为文本框时自动聚焦文本框
      // ! this.$nextTick函数的作用，就是当页面上的元素被重新渲染后，才会调用函数中的方法
      // ! 因为当切换显示文本框时，inputVisible变成true后，还没有立即渲染完成文本框，如果立即调用聚焦，会找不到对象报错，所以要等待渲染完成再调用聚焦方法
      this.$nextTick(_ => {
        // 通过$refs获取到文本框的ref属性名，然后通过focus聚焦
        this.$refs.saveTagInput.$refs.input.focus()
      })
    }

  },
  //! 计算属性 里面的函数必须要有return，否则会报错
  // 如果tag页签需要被禁用，则返回true，（因为我们要根据级联选择器的数组长度判断是否选择了第三级来开放页签的选择）
  computed: {
    // 当前选中的三级分类Id，检测到级联选择器的本地数据长度为3时，就取数组的最后一个
    cateId () {
      if (this.selectedCateKeys.length === 3) {
        return this.selectedCateKeys[2]
      }
      // 没有选择第三级分类时，返回一个null
      return null
    },
    isBtnDiabled () {
      if (this.selectedCateKeys.length !== 3) {
        return true
      } else {
        return false
      }
    },
    // 动态计算添加参数对话框中的标题的文本
    titleText () {
      if (this.activeName === 'many') {
        return '动态参数'
      } else {
        return '静态参数'
      }
    }
  }
}
</script>

<style lang="less" scoped>
  .cat_opt{
  margin: 15px 0;
  }
  .el-tag-myExpandClass{
    margin: 10px;
  }
  .input-new-tag {
    width: 100px;
  }
</style>
